package conf

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/kevinlisr/install-levault/sqldml"
	"github.com/kevinlisr/install-levault/vari"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
	"sync"
	"time"

	_ "github.com/go-sql-driver/mysql"
)

var (
	minioClient *minio.Client
	db          *sql.DB
)

func newConfig() *Config {
	return &Config{
		Minio:       newDefaultMinio(),
		Log:         newDefaultLog(),
		Dmdb:        newDefaultDmdb(),
		Updatedm:    NewDefaultUpdateDm(),
		Updatemysql: NewDefaultUpdateMysql(),
		Packages:    newDefaultPackages(),
	}
}

type Config struct {
	Log         *log         `mapstructure:"log" json:"log" yaml:"log" toml:"log" `
	Minio       *miniosvr    `mapstructure:"minio" json:"minio" yaml:"minio" toml:"minio"`
	Packages    *packages    `mapstructure:"packages" json:"packages" yaml:"packages" toml:"packages"`
	Dmdb        *dmdb        `mapstructure:"dmdb" json:"dmdb" yaml:"dmdb" toml:"dmdb"`
	Updatedm    *updatedm    `mapstructure:"updatedm" json:"updatedm" yaml:"updatedm" toml:"updatedm"`
	Mysqldb     *mysqldb     `mapstructure:"mysqldb" json:"mysqldb" yaml:"mysqldb" toml:"mysqldb"`
	Updatemysql *updatemysql `mapstructure:"updatemysql" json:"updatemysql" yaml:"updatemysql" toml:"updatemysql"`
}

func (c *Config) InitGlobal() error {
	// 加载全局配置单例
	global = c
	return nil
}

type log struct {
	TimeKey  string `toml:"timekey" env:"LOG_TIMEKEY"`
	LevelKey string `toml:"levelkey" env:"LOG_LEVELKEY"`
	NameKey  string `toml:"namekey" env:"LOG_NAMEKEY"`
	PathDir  string `toml:"pathdir" env:"LOG_PATH_DIR"`
}

// BucketName, accessKeyID, secretAccessKey, endpointUrl, PathDir
type miniosvr struct {
	BucketName      string `mapstructure:"bucketname" json:"bucketname" yaml:"bucketname" toml:"bucketname" env:"MINIO_BUCKETNAME"`
	AccessKeyID     string `mapstructure:"accesskey" json:"accesskey" yaml:"accesskey" toml:"accesskey" env:"MINIO_ACCESSKEY"`
	SecretAccessKey string `mapstructure:"secretkey" json:"secretkey" yaml:"secretkey" toml:"secretkey" env:"MINIO_SECRETKEY"`
	Location        string `mapstructure:"location" json:"location" yaml:"location" toml:"location" env:"MINIO_LOCATION"`
	EndpointUrl     string `mapstructure:"endpointurl" json:"endpointurl" yaml:"endpointurl" toml:"endpointurl" env:"MINIO_ENDPOINTURL"`
	PathDir         string `mapstructure:"pathdir" json:"pathdir" yaml:"pathdir" toml:"pathdir" env:"MINIO_PATHDIR"`
	Ssl             bool   `mapstructure:"ssl" json:"ssl" yaml:"ssl" toml:"ssl" env:"MINIO_SSL"`
}

type dmdb struct {
	DataSource string   `mapstructure:"datasource" json:"datasource" yaml:"datasource" toml:"datasource" env:"DMDB_DATASOURCE"`
	DriverName string   `mapstructure:"drivername" json:"drivername" yaml:"drivername" toml:"drivername" env:"DMDB_DRIVERNAME"`
	Schema     string   `mapstructure:"schema" json:"schema" yaml:"schema" toml:"schema" env:"DMDB_SCHEMA"`
	Tables     []string `mapstructure:"tables" json:"tables" yaml:"tables" toml:"tables" env:"DMDB_TABLES"`
}

type mysqldb struct {
	Host        string   `mapstructure:"host" json:"host" yaml:"host" toml:"host" env:"MYSQLDB_HOST"`
	Port        string   `mapstructure:"port" json:"port" yaml:"port" toml:"port" env:"MYSQLDB_PORT"`
	UserName    string   `mapstructure:"username" json:"username" yaml:"username" toml:"username" env:"MYSQLDB_USERNAME"`
	Password    string   `mapstructure:"password" json:"password" yaml:"password" toml:"password" env:"MYSQLDB_PASSWORD"`
	Database    string   `mapstructure:"database" json:"database" yaml:"database" toml:"database" env:"MYSQLDB_DATABASE"`
	MaxOpenConn int      `mapstructure:"maxopenconn" json:"maxopenconn" yaml:"maxopenconn" toml:"maxopenconn" env:"MYSQLDB_MAXOPENCONN"`
	MaxIdleConn int      `mapstructure:"maxidleconn" json:"maxidleconn" yaml:"maxidleconn" toml:"maxidleconn" env:"MYSQLDB_MAXIDLECONN"`
	MaxLifeTime int      `mapstructure:"maxlifetime" json:"maxlifetime" yaml:"maxlifetime" toml:"maxlifetime" env:"MYSQLDB_MAXLIFETIME"`
	MaxIdleTime int      `mapstructure:"maxidletime" json:"maxidletime" yaml:"maxidletime" toml:"maxidletime" env:"MYSQLDB_MAXIDLETIME"`
	Tables      []string `mapstructure:"tables" json:"tables" yaml:"tables" toml:"tables" env:"MYSQL_TABLES"`
	lock        sync.Mutex
}

type updatedm struct {
	Dmlsqls     []string `mapstructure:"dmlsqls" json:"dmlsqls" yaml:"dmlsqls" toml:"dmlsqls" env:"DMDB_DMLSQLS"`
	Dburl       string   `mapstructure:"dburl" json:"dburl" yaml:"dburl" toml:"dburl" env:"DMDB_DBURL"`
	Username    string   `mapstructure:"username" json:"username" yaml:"username" toml:"username" env:"DMDB_USERNAME"`
	Password    string   `mapstructure:"password" json:"password" yaml:"password" toml:"password" env:"DMDB_PASSWORD"`
	Dbtype      string   `mapstructure:"dbtype" json:"dbtype" yaml:"dbtype" toml:"dbtype" env:"DMDB_DBTYPE"`
	Host        string   `mapstructure:"host" json:"host" yaml:"host" toml:"host" env:"DMDB_HOST"`
	Port        int      `mapstructure:"port" json:"port" yaml:"port" toml:"port" env:"DMDB_PORT"`
	MiniourlOld string   `mapstructure:"miniourlold" json:"miniourlold" yaml:"miniourlold" toml:"miniourlold" env:"DMDB_MINIOURLOLD"`
	MiniourlNew string   `mapstructure:"miniourlnew" json:"miniourlnew" yaml:"miniourlnew" toml:"miniourlnew" env:"DMDB_MINIOURLNEW"`
}

type updatemysql struct {
	Dmlsqls     []string `mapstructure:"dmlsqls" json:"dmlsqls" yaml:"dmlsqls" toml:"dmlsqls" env:"DMDB_DMLSQLS"`
	Dburl       string   `mapstructure:"dburl" json:"dburl" yaml:"dburl" toml:"dburl" env:"DMDB_DBURL"`
	Username    string   `mapstructure:"username" json:"username" yaml:"username" toml:"username" env:"DMDB_USERNAME"`
	Password    string   `mapstructure:"password" json:"password" yaml:"password" toml:"password" env:"DMDB_PASSWORD"`
	Dbtype      string   `mapstructure:"dbtype" json:"dbtype" yaml:"dbtype" toml:"dbtype" env:"DMDB_DBTYPE"`
	Host        string   `mapstructure:"host" json:"host" yaml:"host" toml:"host" env:"DMDB_HOST"`
	Port        int      `mapstructure:"port" json:"port" yaml:"port" toml:"port" env:"DMDB_PORT"`
	MiniourlOld string   `mapstructure:"miniourlold" json:"miniourlold" yaml:"miniourlold" toml:"miniourlold" env:"DMDB_MINIOURLOLD"`
	MiniourlNew string   `mapstructure:"miniourlnew" json:"miniourlnew" yaml:"miniourlnew" toml:"miniourlnew" env:"DMDB_MINIOURLNEW"`
}

type packages struct {
	Jdk         string `mapstructure:"jdk" json:"jdk" yaml:"jdk" toml:"jdk"`
	Dm          string `mapstructure:"dm" json:"dm" yaml:"dm" toml:"dm"`
	Minio       string `mapstructure:"minio" json:"minio" yaml:"minio" toml:"minio"`
	Nacos       string `mapstructure:"nacos" json:"nacos" yaml:"nacos" toml:"nacos"`
	Nebulagraph string `mapstructure:"nebulagraph" json:"nebulagraph" yaml:"nebulagraph" toml:"nebulagraph"`
	Redis       string `mapstructure:"redis" json:"redis" yaml:"redis" toml:"redis"`
	Rocketmq    string `mapstructure:"rocketmq" json:"rocketmq" yaml:"rocketmq" toml:"rocketmq"`
	Openresty   string `mapstructure:"openresty" json:"openresty" yaml:"openresty" toml:"openresty"`
	Erlang      string `mapstructure:"erlang" json:"erlang" yaml:"erlang" toml:"erlang"`
	Socat       string `mapstructure:"socat" json:"socat" yaml:"socat" toml:"socat"`
	Rabbitmq    string `mapstructure:"rabbitmq" json:"rabbitmq" yaml:"rabbitmq" toml:"rabbitmq"`
}

func newDefaultMinio() *miniosvr {
	return &miniosvr{
		BucketName:      "default",
		AccessKeyID:     "admin",
		SecretAccessKey: "admin",
		EndpointUrl:     "127.0.0.1",
		PathDir:         "/root/testfile",
	}
}

func newDefaultDmdb() *dmdb {
	return &dmdb{
		DataSource: "dm://SYSDBA:SYSDBA@127.0.0.1:5236",
		DriverName: "dm",
	}
}

func newDefaultMysqlDB() *mysqldb {
	return &mysqldb{
		Database:    "levault",
		Host:        "127.0.0.1",
		Port:        "3306",
		MaxOpenConn: 200,
		MaxIdleConn: 100,
	}
}

func newDefaultPackages() *packages {
	return &packages{}
}

func NewDefaultUpdateDm() *updatedm {
	var UPDATE_SQL []string = []string{
		sqldml.UPDATE_META_DATA_SOURCE_SQL,
		sqldml.UPDATE_ML_DB_INFO_SQL,
		sqldml.UPDATE_IMG_SQL,
		sqldml.UPDATE_OTHER_SQL,
		sqldml.UPDATE_WORD_SQL,
		sqldml.UPDATE_PPT_SQL,
		sqldml.UPDATE_EXCEL_SQL,
		sqldml.UPDATE_ZIP_SQL,
		sqldml.UPDATE_RAR_SQL,
		sqldml.UPDATE_TEXT_SQL,
		sqldml.UPDATE_PDF_SQL,
		sqldml.UPDATE_HELP_DOC_SQL,
		sqldml.UPDATE_RICH_TEXT_SQL,
		sqldml.UPDATE_FILE_SETT_SQL,
	}
	return &updatedm{
		Dmlsqls:     UPDATE_SQL,
		Dburl:       "jdbc:dm://192.168.31.134:5236?schema=LEVAULT",
		Username:    "SYSDBA",
		Password:    "SYSDBA",
		Dbtype:      "DM",
		Host:        "192.168.31.134",
		Port:        5236,
		MiniourlOld: "https://minio-uat.rysaas.cn",
		MiniourlNew: "http://192.168.31.134",
	}
}

func NewDefaultUpdateMysql() *updatemysql {
	var UPDATE_SQL []string = []string{
		sqldml.UPDATE_META_DATA_SOURCE_SQL,
		sqldml.UPDATE_ML_DB_INFO_SQL,
	}
	return &updatemysql{
		Dmlsqls:     UPDATE_SQL,
		Dburl:       "192.168.5.118",
		Username:    "root",
		Password:    "1",
		Dbtype:      "MYSQL",
		Host:        "192.168.5.118",
		Port:        3306,
		MiniourlOld: "",
		MiniourlNew: "",
	}
}

// Client 获取一个全局的mongodb客户端连接
func (m *miniosvr) Client() (*minio.Client, error) {
	// 加载全局数据量单例
	if minioClient == nil {
		client, err := m.getClient()
		if err != nil {
			return nil, err
		}
		minioClient = client
	}

	return minioClient, nil
}

func (m *miniosvr) getClient() (*minio.Client, error) {
	client, err := minio.New(m.EndpointUrl, &minio.Options{
		Creds:  credentials.NewStaticV4(m.AccessKeyID, m.SecretAccessKey, ""),
		Secure: m.Ssl,
	})
	if err != nil {
		vari.Logger.Error(fmt.Sprintf("Init Minio Client Failed, %s", err.Error()))
		return nil, err
	}
	fmt.Println("Minio初始化完成！")
	return client, nil
}

func newDefaultLog() *log {
	return &log{
		TimeKey:  "T",
		LevelKey: "L",
		NameKey:  "log",
		PathDir:  "/logs",
	}
}

// dm
func (d *dmdb) GetDmDB() (db *sql.DB, err error) {
	//if db, err = connect(d.DriverName, d.DataSource); err != nil {
	//	fmt.Println(err)
	//	return nil, err
	//}
	//return db, nil

	// 可以简写
	return connect(d.DriverName, d.DataSource)
}

/* 创建数据库连接 */
func connect(driverName string, dataSourceName string) (*sql.DB, error) {
	var db *sql.DB
	var err error
	if db, err = sql.Open(driverName, dataSourceName); err != nil {
		return nil, err
	}
	if err = db.Ping(); err != nil {
		return nil, err
	}
	vari.Logger.Info(fmt.Sprintf("connect to \"%s\" succeed.", dataSourceName))
	return db, nil
}

// getDBConn use to get db connection pool
func (m *mysqldb) getDBConn() (*sql.DB, error) {
	var err error
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&multiStatements=true", m.UserName, m.Password, m.Host, m.Port, m.Database)
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return nil, fmt.Errorf("connect to mysql<%s> error, %s", dsn, err.Error())
	}
	db.SetMaxOpenConns(m.MaxOpenConn)
	db.SetMaxIdleConns(m.MaxIdleConn)
	if m.MaxLifeTime != 0 {
		db.SetConnMaxLifetime(time.Second * time.Duration(m.MaxLifeTime))
	}
	if m.MaxIdleConn != 0 {
		db.SetConnMaxIdleTime(time.Second * time.Duration(m.MaxIdleTime))
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := db.PingContext(ctx); err != nil {
		return nil, fmt.Errorf("ping mysql<%s> error, %s", dsn, err.Error())
	}
	return db, nil
}

func (m *mysqldb) GetDB() (*sql.DB, error) {
	// 加载全局数据量单例
	m.lock.Lock()
	defer m.lock.Unlock()
	if db == nil {
		conn, err := m.getDBConn()
		if err != nil {
			return nil, err
		}
		db = conn
	}
	return db, nil
}
